//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//

#include "MobiSense.h"
#include "lbr/CycleTargetArea.h"
#include "lbr/Split.h"
#include "Coordinates.h"
#include "BoundaryList.h"

#include "PastryRoutingTable.h"
#include "NodeHandle.h"
#include "OverlayKey.h"

#include "MobiSenseBrokerMessage_m.h"
#include "MobiSenseAppMessage_m.h"

#include <map>
#include <string>

using namespace std;

Define_Module(MobiSense);

MobiSense::~MobiSense() {
	// TODO Auto-generated destructor stub

}

void MobiSense::initializeOverlay(int stage) {

	Pastry::initializeOverlay(stage);

	if (stage != MIN_STAGE_OVERLAY)
		return;

	bl = check_and_cast<BoundaryList*>
	        (getParentModule()->getSubmodule("boundaryList"));

	// LB services
	boundaries = par("boundaries").stdstringValue();

}

void MobiSense::handleBrokerLBMulcast(MobiSenseBrokerLBMulticastPkt* msg)
{
	CycleTargetArea &area = (check_and_cast<MobiSenseAppSub*>(msg->getEncapsulatedMsg()))->getArea();
	int level = msg->getLevel();

	bool isSelfOverlap = false;

	for(int i= level; i < bl->size(); i++ ){
		const Split &split = bl->getSplit(i);
		Side side = area.onWhichSide(split);

		if(side != split.getSide()) // on the other side or both
		{
			// send a copy of the msg
			MobiSenseBrokerLBMulticastPkt* newmsg = msg->dup();
			newmsg->setLevel(i+1);
			const OverlayKey &next = this->findLevelNode(level);
			this->route(next, OVERLAY_COMP, OVERLAY_COMP, newmsg);
			if(side != BOTH){ // no self overlap, no more check
				isSelfOverlap = false;
				break;
			}else{
				isSelfOverlap = true;
			}
		}
	}
	if(isSelfOverlap){ // self is one of the target node
		// deliver msg to upper layer
		EV << "[MobiSense::handleBrokerLBMulcast() @ " << thisNode.getAddress()
		       << " (" << thisNode.getKey().toString(16) << ")]\n"
		       << "  receive subscription"
		       << endl;
	}

	delete msg;
}

void MobiSense::handleBrokerLBUnicast(MobiSenseBrokerLBUnicastPkt* msg)
{
	Coordinates &coord = (check_and_cast<MobiSenseAppPub*>(msg->getEncapsulatedMsg()))->getCoordinate();
	int level = msg->getLevel();

	const Split &split = bl->getSplit(level);
	Side side = coord.onWhichSide(split);

	if (side != split.getSide()) // on the other side
	{
		// send a copy of the msg
		MobiSenseBrokerLBUnicastPkt *newmsg = msg->dup();
		newmsg->setLevel(level + 1);
		const OverlayKey &next = this->findLevelNode(level);
		this->route(next, OVERLAY_COMP, OVERLAY_COMP, newmsg);
	}
	else{ // destination reached
		//deliver to upper layer
		EV << "[MobiSense::handleBrokerLBUnicast() @ " << thisNode.getAddress()
				       << " (" << thisNode.getKey().toString(16) << ")]\n"
				       << "  receive msg "
				       << endl;
	}

	delete msg;
}

/*void MobiSense::handleNotifyBroker(BrokerNOTIFY *msg)
{

	map<OverlayKey, vector<OverlayKey> > nextHop;
	for(int i=0;i<msg->getDestIDArraySize();i++){
		const OverlayKey &dest = msg->getDestID(i);
		const OverlayKey &next = this->routingTable->lookupNextHop(dest);
		if(next == thisNode.key){ // self is one of the dest
			// deliver msg to upper layer
		}
		else
		{
			map<OverlayKey, vector<OverlayKey> >::iterator iter = nextHop.find(next);
			if(iter == nextHop.end()){
				vector<OverlayKey> v;
				v.push_back(dest);
				nextHop.insert(make_pair(next, v));
			}else{
				vector<OverlayKey> &vec = check_and_cast<vector<OverlayKey> >(iter->second);
				vec.push_back(dest);
			}
		}
	}
	// for each next hop, route a copy of the msg
	for( map<OverlayKey, vector<OverlayKey> >::const_iterator iter = nextHop.begin(); iter != nextHop.end(); ++iter ){
		BrokerNOTIFY* newmsg = new BrokerNOTIFY(msg->getName());

		vector<OverlayKey> &dests = check_and_cast<vector<OverlayKey> >(iter->second);

		newmsg->setDestIDArraySize(dests.size());
		for(int j=0;j < dests.size(); j++){
			newmsg->setDestID(j, dests.at(j));
		}

		newmsg->setBitLength(BROKERNOTIFY_L(newmsg));

		this->route(*iter, OVERLAY_COMP, OVERLAY_COMP, newmsg);
		delete newmsg;
	}

	delete msg;
}*/

void MobiSense::handleBrokerPkt(MobiSenseBrokerPkt *msg)
{
	if(msg->getType() == MOBISENSE_BRO_LBMUL){
		this->handleBrokerLBMulcast(dynamic_cast<MobiSenseBrokerLBMulticastPkt*>(msg));
	}else if(msg->getType() == MOBISENSE_BRO_LBUNI){
		this->handleBrokerLBUnicast(dynamic_cast<MobiSenseBrokerLBUnicastPkt*>(msg));
	}

}

void MobiSense::changeState(int toState)
{
	Pastry::changeState(toState);
	switch (toState) {
	    case INIT:
	    	bl->initializeList(boundaries, thisNode);
	        break;

	    case JOINING_2:

	        break;
	    }
}

const OverlayKey & MobiSense::findLevelNode(int level)
{
	OverlayKey randomDestKey(OverlayKey::random());
	randomDestKey.setKeyLength(this->thisNode.getKey().getLength());
	for(int i=0;i<level ;i++){
		int pos1 = thisNode.getKey().getLength() - i - 1;
		randomDestKey.setBit(pos1, thisNode.getKey().getBit(pos1));
	}
	int pos2 = thisNode.getKey().getLength() - level - 1; // pos 0 in OverlayKey is the least significant bit
	randomDestKey.setBit(pos2, !thisNode.getKey().getBit(pos2));

	// lookup in routing table
	const NodeHandle& node = this->routingTable->lookupNextHop(randomDestKey);
	if(node.isUnspecified())
		return *(new OverlayKey(randomDestKey));
	else
		return node.getKey();
}

void MobiSense::handleAppMessage(cMessage *msg){

	if(dynamic_cast<MobiSenseAppPkt*>(msg) == NULL){
		return;
	}
	int type = (dynamic_cast<MobiSenseAppPkt*>(msg))->getType();
	if(type == MOBISENSE_APP_SUB){
		MobiSenseAppSub *sub = check_and_cast<MobiSenseAppSub*>(msg);
		MobiSenseBrokerLBMulticastPkt *submsg = new MobiSenseBrokerLBMulticastPkt("lbm");
		submsg->setType(MOBISENSE_BRO_LBMUL);
		submsg->setLevel(0);
		submsg->setSrc(thisNode.getKey());
		submsg->setBitLength(MOBISENSEBROKER_LBMUL_L(submsg));
		submsg->encapsulate(sub);
		this->handleBrokerLBMulcast(submsg);
	}else if(type == MOBISENSE_APP_PUB){
		MobiSenseAppPub *pub = check_and_cast<MobiSenseAppPub*>(msg);
		MobiSenseBrokerLBUnicastPkt *pubmsg = new MobiSenseBrokerLBUnicastPkt("lbu");
		pubmsg->setType(MOBISENSE_BRO_LBUNI);
		pubmsg->setLevel(0);
		pubmsg->setBitLength(MOBISENSEBROKER_LBUNI_L(pubmsg));
		pubmsg->encapsulate(pub);
		this->handleBrokerLBUnicast(pubmsg);
	}
}

void MobiSense::callDeliver(BaseOverlayMessage* msg,
                              const OverlayKey& destKey)
{
    KBRdeliver* deliverMsg = new KBRdeliver();

    OverlayCtrlInfo* overlayCtrlInfo =
        check_and_cast<OverlayCtrlInfo*>(msg->removeControlInfo());

    BaseAppDataMessage* appDataMsg = dynamic_cast<BaseAppDataMessage*>(msg);

    // hook for broker msg handling
    if (dynamic_cast<MobiSenseBrokerPkt*>(msg->getEncapsulatedMsg()) != NULL){
    	this->handleBrokerPkt(dynamic_cast<MobiSenseBrokerPkt*>(msg->decapsulate()));
    	//delete msg;
    	return;
    }

    // TODO GIA
    if (appDataMsg != NULL) {
        overlayCtrlInfo->setSrcComp(appDataMsg->getSrcComp());
        overlayCtrlInfo->setDestComp(appDataMsg->getDestComp());
    }

    deliverMsg->setControlInfo(overlayCtrlInfo);
    deliverMsg->setDestKey(destKey);
    deliverMsg->encapsulate(msg->decapsulate());
    deliverMsg->setType(KBR_DELIVER);

    cGate* destGate = getCompRpcGate(static_cast<CompType>(
            overlayCtrlInfo->getDestComp()));

    if (destGate == NULL) {
        throw cRuntimeError("BaseOverlay::callDeliver(): Unknown destComp!");
    }

    sendDirect(deliverMsg, destGate);

    delete msg;
}








